feat: Login 페이지 구현 및 TextButton / GNB 공통 컴포넌트 구현 (#37) #74
feat: Login 페이지 구현 및 TextButton / GNB 공통 컴포넌트 구현 (#37) #74wkdtnqls0506 merged 14 commits intomainfrom
Conversation
|
""" Walkthrough로그인 페이지가 완전히 새롭게 디자인되고, 관련 스타일과 UI 컴포넌트(GNB, TextButton 등)가 대거 추가되었습니다. Storybook 설정이 Vite에서 Webpack 기반으로 전환되었고, 스타일 시스템은 rem 단위로 통일되었습니다. 여러 신규 컴포넌트와 스토리북 스토리가 도입되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant LoginPage
participant GNB
participant TextButton
participant KakaoOAuth
User->>LoginPage: 페이지 접속
LoginPage->>GNB: GNB 렌더링(좌측: 없음, 우측: 둘러보기)
User->>GNB: "둘러보기" 클릭
GNB->>LoginPage: 홈으로 라우팅
LoginPage->>TextButton: Kakao 로그인 버튼 렌더링
User->>TextButton: Kakao 로그인 버튼 클릭
TextButton->>KakaoOAuth: 로그인 함수 호출
Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related PRs
Poem
""" Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
src/components/ui/Text/Text.css.tsOops! Something went wrong! :( ESLint: 9.30.1 ESLint couldn't find the plugin "eslint-plugin-react-hooks". (The package "eslint-plugin-react-hooks" was not found when loaded as a Node module from the directory "".) It's likely that the plugin isn't installed correctly. Try reinstalling by running the following: The plugin "eslint-plugin-react-hooks" was referenced from the config file in " » eslint-config-next/core-web-vitals » /node_modules/.pnpm/eslint-config-next@15.3.2_eslint@9.30.1_jiti@1.21.7__typescript@5.8.3/node_modules/eslint-config-next/index.js". If you still can't figure out the problem, please see https://eslint.org/docs/latest/use/troubleshooting. 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
|
🎨 Storybook Preview: https://685a32a1c0bbd269fdb67af4-nssreoytxh.chromatic.com/ |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (6)
src/components/ui/GNB/SubGNB/SubGNB.tsx (1)
68-89: 조건부 로직 개선 제안컴포넌트 구현은 전반적으로 잘 되어 있으나, 조건부 className 할당 로직을 더 간단하게 만들 수 있습니다.
다음과 같이 리팩토링하여 가독성을 향상시킬 수 있습니다:
- className={ - leftAddon && title && align === "left" - ? `${styles.leftWrapper} ${styles.leftWrapperWithMargin}` - : styles.leftWrapper - } + className={`${styles.leftWrapper} ${ + leftAddon && title && align === "left" ? styles.leftWrapperWithMargin : "" + }`}src/app/(auth)/login/_styles/Login.css.ts (2)
44-57: 그라데이션 오버레이 구현 검토그라데이션 오버레이가 잘 구현되어 있습니다. 다만 하드코딩된 색상 값들(
rgba(255, 245, 214, 0),rgba(255, 246, 218, 1),rgba(255, 255, 255, 1))을 semantic 토큰으로 대체하는 것을 고려해보세요.
91-94: 카카오 버튼 색상 하드코딩카카오 버튼의 배경색이 하드코딩되어 있습니다. 브랜드 색상이므로 semantic 토큰에 정의하거나 상수로 분리하는 것이 좋겠습니다.
export const kakaoButton = style({ - backgroundColor: "#FAE300", + backgroundColor: semantic.brand.kakao, // 또는 별도 상수로 정의 gap: "0.8rem", });src/components/ui/GNB/SubGNB/SubGNB.css.ts (1)
61-61: 단위 일관성 문제
minWidth: "2.4em"이 다른 곳에서 사용하는rem단위와 일관성이 없습니다."2.4rem"으로 변경하는 것이 좋겠습니다.export const rightWrapper = style({ - minWidth: "2.4em", + minWidth: "2.4rem", display: "flex", alignItems: "center", justifyContent: "flex-end", gap: "1.6rem", });src/components/ui/TextButton/TextButton.css.ts (1)
6-6: 스타일 이름 충돌 가능성을 확인해주세요.
src/app/(auth)/login/_styles/Login.css.ts에도textButton이라는 이름의 스타일이 정의되어 있습니다. 동일한 이름으로 인한 혼란을 방지하기 위해 더 구체적인 이름(예:textButtonRecipe)을 사용하는 것을 고려해보세요.-export const textButton = recipe({ +export const textButtonRecipe = recipe({.storybook/main.ts (1)
1-1: ESLint 비활성화가 정말 필요한지 확인해주세요.파일 전체에
@typescript-eslint/no-explicit-any를 비활성화하는 것보다 필요한 부분에만 제한적으로 적용하는 것이 좋습니다.ESLint 비활성화를 제거하고 대신 webpack 설정 타입을 개선해보세요:
-/* eslint-disable @typescript-eslint/no-explicit-any */ -import type { StorybookConfig } from "@storybook/nextjs"; +import type { StorybookConfig } from "@storybook/nextjs"; +import type { Configuration } from "webpack";
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (9)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlpublic/images/login-background.pngis excluded by!**/*.pngpublic/images/login-house.pngis excluded by!**/*.pngsrc/assets/check.svgis excluded by!**/*.svgsrc/assets/kakao-logo.svgis excluded by!**/*.svgsrc/assets/korean-orange-logo.svgis excluded by!**/*.svgsrc/assets/left-arrow.svgis excluded by!**/*.svgsrc/assets/menu.svgis excluded by!**/*.svgsrc/assets/search.svgis excluded by!**/*.svg
📒 Files selected for processing (28)
.storybook/main.ts(1 hunks).storybook/preview.ts(1 hunks).storybook/vitest.setup.ts(0 hunks)package.json(4 hunks)src/app/(auth)/login/_styles/Login.css.ts(1 hunks)src/app/(auth)/login/page.tsx(1 hunks)src/components/ui/Bleed/Bleed.stories.tsx(1 hunks)src/components/ui/BottomSheet/BottomSheet.css.ts(3 hunks)src/components/ui/BottomSheet/BottomSheet.stories.tsx(1 hunks)src/components/ui/Button/Button.css.ts(2 hunks)src/components/ui/Button/Button.stories.tsx(1 hunks)src/components/ui/Button/Button.tsx(1 hunks)src/components/ui/GNB/SubGNB/SubGNB.css.ts(1 hunks)src/components/ui/GNB/SubGNB/SubGNB.stories.tsx(1 hunks)src/components/ui/GNB/SubGNB/SubGNB.tsx(1 hunks)src/components/ui/GNB/SubGNB/index.ts(1 hunks)src/components/ui/Spacer/Spacer.stories.tsx(1 hunks)src/components/ui/Stack/Stack.stories.tsx(1 hunks)src/components/ui/Text/Text.stories.tsx(1 hunks)src/components/ui/TextButton/TextButton.css.ts(1 hunks)src/components/ui/TextButton/TextButton.stories.tsx(1 hunks)src/components/ui/TextButton/TextButton.tsx(1 hunks)src/components/ui/TextButton/index.ts(1 hunks)src/components/ui/TextField/TextField.css.ts(3 hunks)src/components/ui/TextField/TextField.stories.tsx(2 hunks)src/styles/reset.css.ts(1 hunks)src/styles/semantic.css.ts(1 hunks)src/styles/typography.css.ts(1 hunks)
💤 Files with no reviewable changes (1)
- .storybook/vitest.setup.ts
🧰 Additional context used
📓 Path-based instructions (3)
`{src/app/**/_components/*.{ts,tsx},src/components/**/*.{ts,tsx}}`: 컴포넌트 파일은 PascalCase로 네이밍해야 한다 (예: `Button.tsx`, `DomainLayout.tsx`).
{src/app/**/_components/*.{ts,tsx},src/components/**/*.{ts,tsx}}: 컴포넌트 파일은 PascalCase로 네이밍해야 한다 (예:Button.tsx,DomainLayout.tsx).
📄 Source: CodeRabbit Inference Engine (.cursor/rules/nextjs-folder-structure.mdc)
List of files the instruction was applied to:
src/components/ui/Text/Text.stories.tsxsrc/components/ui/BottomSheet/BottomSheet.stories.tsxsrc/components/ui/Bleed/Bleed.stories.tsxsrc/components/ui/Spacer/Spacer.stories.tsxsrc/components/ui/Button/Button.stories.tsxsrc/components/ui/Stack/Stack.stories.tsxsrc/components/ui/TextField/TextField.stories.tsxsrc/components/ui/GNB/SubGNB/index.tssrc/components/ui/TextButton/index.tssrc/components/ui/TextButton/TextButton.tsxsrc/components/ui/TextField/TextField.css.tssrc/components/ui/Button/Button.css.tssrc/components/ui/BottomSheet/BottomSheet.css.tssrc/components/ui/GNB/SubGNB/SubGNB.tsxsrc/components/ui/GNB/SubGNB/SubGNB.stories.tsxsrc/components/ui/GNB/SubGNB/SubGNB.css.tssrc/components/ui/TextButton/TextButton.stories.tsxsrc/components/ui/Button/Button.tsxsrc/components/ui/TextButton/TextButton.css.ts
`**/*.css.ts`: vanilla-extract 스타일 파일은 camelCase로, `.css.ts` 확장자를 사용해야 하며, 해당 컴포넌트와 같은 폴더에 배치해야 한다.
**/*.css.ts: vanilla-extract 스타일 파일은 camelCase로,.css.ts확장자를 사용해야 하며, 해당 컴포넌트와 같은 폴더에 배치해야 한다.
📄 Source: CodeRabbit Inference Engine (.cursor/rules/nextjs-folder-structure.mdc)
List of files the instruction was applied to:
src/styles/reset.css.tssrc/styles/semantic.css.tssrc/styles/typography.css.tssrc/components/ui/TextField/TextField.css.tssrc/components/ui/Button/Button.css.tssrc/components/ui/BottomSheet/BottomSheet.css.tssrc/app/(auth)/login/_styles/Login.css.tssrc/components/ui/GNB/SubGNB/SubGNB.css.tssrc/components/ui/TextButton/TextButton.css.ts
`src/styles/*`: 전역 스타일 파일은 `styles/` 폴더에 위치해야 한다.
src/styles/*: 전역 스타일 파일은styles/폴더에 위치해야 한다.
📄 Source: CodeRabbit Inference Engine (.cursor/rules/nextjs-folder-structure.mdc)
List of files the instruction was applied to:
src/styles/reset.css.tssrc/styles/semantic.css.tssrc/styles/typography.css.ts
🧠 Learnings (14)
.storybook/preview.ts (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to middleware.ts : Next.js 미들웨어는 프로젝트 루트의 `middleware.ts`로 관리해야 한다.
src/components/ui/Bleed/Bleed.stories.tsx (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to middleware.ts : Next.js 미들웨어는 프로젝트 루트의 `middleware.ts`로 관리해야 한다.
src/styles/reset.css.ts (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/styles/* : 전역 스타일 파일은 `styles/` 폴더에 위치해야 한다.
src/components/ui/Spacer/Spacer.stories.tsx (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to middleware.ts : Next.js 미들웨어는 프로젝트 루트의 `middleware.ts`로 관리해야 한다.
src/components/ui/Button/Button.stories.tsx (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/app/**/_components/*.{ts,tsx},src/components/**/*.{ts,tsx}} : 컴포넌트 파일은 PascalCase로 네이밍해야 한다 (예: `Button.tsx`, `DomainLayout.tsx`).
src/components/ui/Stack/Stack.stories.tsx (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to middleware.ts : Next.js 미들웨어는 프로젝트 루트의 `middleware.ts`로 관리해야 한다.
src/components/ui/TextButton/index.ts (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/app/**/_components/*.{ts,tsx},src/components/**/*.{ts,tsx}} : 컴포넌트 파일은 PascalCase로 네이밍해야 한다 (예: `Button.tsx`, `DomainLayout.tsx`).
src/components/ui/TextButton/TextButton.tsx (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/app/**/_components/*.{ts,tsx},src/components/**/*.{ts,tsx}} : 컴포넌트 파일은 PascalCase로 네이밍해야 한다 (예: `Button.tsx`, `DomainLayout.tsx`).
src/app/(auth)/login/page.tsx (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/app/**/_components/*.{ts,tsx},src/components/**/*.{ts,tsx}} : 컴포넌트 파일은 PascalCase로 네이밍해야 한다 (예: `Button.tsx`, `DomainLayout.tsx`).
src/app/(auth)/login/_styles/Login.css.ts (2)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to **/*.css.ts : vanilla-extract 스타일 파일은 camelCase로, `.css.ts` 확장자를 사용해야 하며, 해당 컴포넌트와 같은 폴더에 배치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: 스타일 근접성: vanilla-extract 스타일 파일은 해당 컴포넌트와 같은 폴더에 배치해야 한다.
src/components/ui/GNB/SubGNB/SubGNB.css.ts (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to **/*.css.ts : vanilla-extract 스타일 파일은 camelCase로, `.css.ts` 확장자를 사용해야 하며, 해당 컴포넌트와 같은 폴더에 배치해야 한다.
src/components/ui/TextButton/TextButton.stories.tsx (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/app/**/_components/*.{ts,tsx},src/components/**/*.{ts,tsx}} : 컴포넌트 파일은 PascalCase로 네이밍해야 한다 (예: `Button.tsx`, `DomainLayout.tsx`).
.storybook/main.ts (3)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to next.config.js : Next.js 설정 파일은 프로젝트 루트의 `next.config.js`로 관리해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to **/*.css.ts : vanilla-extract 스타일 파일은 camelCase로, `.css.ts` 확장자를 사용해야 하며, 해당 컴포넌트와 같은 폴더에 배치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to middleware.ts : Next.js 미들웨어는 프로젝트 루트의 `middleware.ts`로 관리해야 한다.
src/components/ui/TextButton/TextButton.css.ts (2)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to **/*.css.ts : vanilla-extract 스타일 파일은 camelCase로, `.css.ts` 확장자를 사용해야 하며, 해당 컴포넌트와 같은 폴더에 배치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/app/**/_components/*.{ts,tsx},src/components/**/*.{ts,tsx}} : 컴포넌트 파일은 PascalCase로 네이밍해야 한다 (예: `Button.tsx`, `DomainLayout.tsx`).
🧬 Code Graph Analysis (6)
src/app/(auth)/login/page.tsx (1)
src/app/(auth)/_api/auth/auth.api.ts (1)
redirectToKakaoOAuthLoginPage(42-44)
src/components/ui/Button/Button.css.ts (1)
src/styles/typography.css.ts (1)
typography(3-148)
src/components/ui/BottomSheet/BottomSheet.css.ts (2)
src/styles/typography.css.ts (1)
typography(3-148)src/styles/semantic.css.ts (1)
semantic(5-68)
src/components/ui/GNB/SubGNB/SubGNB.tsx (2)
src/components/ui/GNB/SubGNB/index.ts (2)
SubGNBProps(1-1)SubGNB(1-1)src/components/ui/GNB/SubGNB/SubGNB.css.ts (1)
title(53-58)
src/components/ui/TextButton/TextButton.stories.tsx (2)
src/components/ui/TextButton/TextButton.tsx (1)
TextButton(28-46)src/components/ui/TextButton/index.ts (1)
TextButton(1-1)
src/components/ui/TextButton/TextButton.css.ts (1)
src/app/(auth)/login/_styles/Login.css.ts (1)
textButton(25-27)
🪛 GitHub Actions: Storybook Chromatic Deployment
src/components/ui/TextField/TextField.stories.tsx
[error] 1-1: Build failed: Module not found error - Can't resolve '@/assets/clear.svg'. This caused the Storybook build to fail with exit code 1.
src/components/ui/TextField/TextField.css.ts
[error] 1-1: Build failed: Module not found error - Can't resolve '@/assets/clear.svg'. This caused the Storybook build to fail with exit code 1.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: test
🔇 Additional comments (41)
src/styles/reset.css.ts (1)
5-7: rem 단위 시스템을 위한 적절한 기본 설정입니다.HTML 요소에
fontSize: "62.5%"를 설정하는 것은 rem 단위 계산을 쉽게 하기 위한 표준 방법입니다 (1rem = 10px). 이는 typography.css.ts의 rem 단위 변환과 완벽하게 일치합니다.src/styles/semantic.css.ts (1)
18-29: 버튼 컴포넌트를 위한 체계적인 색상 토큰 구성입니다.버튼 관련 시맨틱 색상들을 별도 섹션으로 구성하여 일관성을 높였습니다. 네이밍 컨벤션(primaryNormalSolid, textPrimary 등)이 직관적이고 기존 색상 토큰 구조와 잘 맞습니다.
src/styles/typography.css.ts (1)
5-6: rem 단위 시스템으로의 일관된 변환입니다.모든 타이포그래피 토큰이 px에서 rem 단위로 정확하게 변환되었습니다. 이는 reset.css.ts의 기본 폰트 크기 설정과 완벽하게 일치하며, 반응형 디자인과 접근성 향상에 도움이 됩니다.
src/components/ui/BottomSheet/BottomSheet.stories.tsx (1)
1-1: Storybook 프레임워크 마이그레이션에 따른 올바른 import 변경입니다.Vite에서 Webpack 기반 Storybook으로의 마이그레이션에 맞춰 import 경로가 정확하게 업데이트되었습니다.
src/components/ui/Text/Text.stories.tsx (1)
1-1: 일관된 Storybook 마이그레이션 적용입니다.모든 Storybook 파일에서 일관되게 적용된 import 변경으로, Webpack 기반 Storybook 설정과 완벽히 호환됩니다.
src/components/ui/Spacer/Spacer.stories.tsx (1)
1-1: Storybook 타입 import 경로 전환 확인 완료
Vite → Webpack 마이그레이션에 따라"@storybook/nextjs"사용이 맞습니다. 다른 변경 사항 없이 정상 동작할 것으로 보입니다.남아 있을 수 있는
@storybook/nextjs-vite의존성 및 임포트가 더 없는지 한번rg검색으로 확인해 주세요.src/components/ui/Stack/Stack.stories.tsx (1)
1-1: import 경로 수정 OK
Webpack 기반 설정과 일치합니다. 추가 조치 필요 없습니다..storybook/preview.ts (1)
3-3: Preview 타입 import 경로 변경 확인
@storybook/nextjs로의 단일 변경이며, 나머지 설정과 충돌되지 않습니다.src/components/ui/Bleed/Bleed.stories.tsx (1)
1-1: 경로 업데이트 적절
Bleed 스토리 또한 동일하게 변경되었습니다. 일관성 유지 👍src/components/ui/Button/Button.stories.tsx (1)
1-1: Button 스토리북 import 경로 교체 완료
모듈 전환에 따른 필수 변경으로 보이며, 문제 없습니다.src/components/ui/GNB/SubGNB/index.ts (1)
1-1: 표준적인 컴포넌트 export 패턴을 잘 따르고 있습니다.SubGNB 컴포넌트와 타입을 깔끔하게 re-export하는 구조로, 다른 파일에서 import하기 쉽도록 설계되었습니다.
src/components/ui/TextButton/index.ts (1)
1-1: 일관된 컴포넌트 export 구조를 유지하고 있습니다.다른 UI 컴포넌트들과 동일한 패턴으로 구현되어 코드베이스의 일관성을 유지하고 있습니다.
src/components/ui/TextField/TextField.stories.tsx (2)
1-1: Storybook 설정 변경이 올바르게 반영되었습니다.Webpack 기반 Storybook 설정으로의 전환에 맞춰 import 경로가 정확히 업데이트되었습니다.
50-50: 조건부 렌더링 로직이 올바르게 구현되었습니다.입력값이 있고 비활성화 상태가 아닐 때만 ClearIcon을 표시하는 로직이 적절합니다.
src/components/ui/TextField/TextField.css.ts (3)
9-9: rem 단위 사용으로 스타일 시스템이 개선되었습니다.픽셀 단위에서 rem 단위로의 변경은 확장성과 접근성을 향상시킵니다.
20-21: 입력 필드의 크기와 패딩이 일관되게 업데이트되었습니다.높이와 패딩 값이 rem 단위로 변경되어 전체 디자인 시스템과 일관성을 유지합니다.
86-86: 우측 애드온 위치와 패딩이 적절히 조정되었습니다.right 위치와 padding 값이 rem 단위로 변경되어 전체 스타일 시스템과 일관성을 유지합니다.
Also applies to: 91-91
src/components/ui/Button/Button.css.ts (3)
3-3: typography 토큰 import 추가 승인디자인 시스템 일관성을 위한 typography 토큰 import가 적절하게 추가되었습니다.
42-42: custom variant 추가 승인custom variant의 빈 객체 추가로 유연한 커스터마이징이 가능해졌습니다. TextButton 컴포넌트와의 일관성도 유지되었습니다.
46-47: typography 토큰 및 rem 단위 적용 승인typography 토큰을 활용한 일관된 폰트 스타일링과 rem 단위 변환이 적절하게 적용되었습니다. 이는 디자인 시스템의 일관성과 접근성을 향상시킵니다.
Also applies to: 51-52, 56-57, 61-63
src/components/ui/BottomSheet/BottomSheet.css.ts (1)
16-16: rem 단위 변환 승인px에서 rem으로의 단위 변환이 일관되게 적용되었습니다. 이는 반응형 디자인과 접근성 향상에 도움이 됩니다. 변환 비율도 올바르게 적용되었습니다.
Also applies to: 27-27, 34-34, 38-39, 42-42, 47-48, 56-57, 72-72
src/components/ui/TextButton/TextButton.tsx (3)
1-13: 타입 정의 및 import 승인TypeScript 타입 정의가 적절하게 구성되었으며, ComponentProps<"button">을 활용한 props 확장이 올바르게 구현되었습니다.
15-26: JSDoc 문서화 승인컴포넌트의 용도와 사용법이 명확하게 문서화되어 있으며, 실제 사용 예시도 포함되어 있어 개발자 경험을 향상시킵니다.
28-46: 컴포넌트 구현 승인props 기본값 설정, 조건부 렌더링, className 조합 등이 올바르게 구현되었습니다. leftAddon의 span 래핑과 children의 span 래핑도 일관성 있게 처리되었습니다.
src/app/(auth)/login/page.tsx (3)
3-4: Next.js 패턴 적용 승인Next.js의 Image 컴포넌트와 useRouter 훅을 적절하게 활용하여 성능과 네비게이션을 올바르게 구현했습니다.
Also applies to: 16-16
6-13: 컴포넌트 및 asset import 승인새로운 UI 컴포넌트들과 SVG asset들이 적절하게 import되어 있으며, 모듈 구조가 깔끔하게 정리되어 있습니다.
22-79: 로그인 페이지 UI 구현 승인SubGNB, TextButton, Button 등의 새로운 컴포넌트들을 활용한 로그인 페이지 구현이 잘 되어 있습니다. 레이아웃 구조가 논리적이며, 한국어 텍스트와 카카오 로그인 연동도 적절하게 구현되었습니다.
src/components/ui/GNB/SubGNB/SubGNB.tsx (2)
1-35: 타입 정의 및 Props 인터페이스 승인AlignVariant와 WrapperVariants 타입 정의가 명확하며, SubGNBProps 인터페이스가 잘 구조화되어 있습니다. JSDoc 주석도 각 prop의 용도를 명확히 설명하고 있습니다.
37-58: JSDoc 문서화 승인컴포넌트의 용도와 사용법이 상세하게 문서화되어 있으며, 실제 사용 예시가 포함되어 개발자 경험을 크게 향상시킵니다.
package.json (3)
26-26: ky 패키지 버전 다운그레이드 확인 필요
ky패키지가 이전 버전으로 다운그레이드되었습니다. 이것이 의도된 변경인지, 특정 호환성 문제로 인한 것인지 확인이 필요합니다.
43-43: Storybook Webpack 마이그레이션 잘 적용됨Vite에서 Webpack 기반 Storybook으로의 마이그레이션이 package.json에 올바르게 반영되었습니다. 이는 SVG 컴포넌트 임포트 문제 해결을 위한 변경사항과 일치합니다.
56-57: vanilla-extract 플러그인 전환 확인vanilla-extract 플러그인이 Vite에서 Webpack으로 올바르게 전환되었습니다. 이는 전체 마이그레이션 전략과 일치합니다.
src/components/ui/GNB/SubGNB/SubGNB.stories.tsx (3)
1-7: SVG 컴포넌트 임포트 성공적으로 작동SVG 파일들이 React 컴포넌트로 성공적으로 임포트되고 있습니다. 이는 Webpack 마이그레이션이 SVG 처리 문제를 해결했음을 확인해줍니다.
9-45: 포괄적인 Storybook 설정argTypes 설정이 매우 상세하고 각 prop에 대한 설명이 명확합니다. 특히
leftAddon과rightAddon의 control을 false로 설정한 것은 React 엘리먼트 특성상 적절한 선택입니다.
47-193: 다양한 사용 사례를 보여주는 스토리들SubGNB 컴포넌트의 다양한 사용 사례를 체계적으로 보여주는 스토리들이 잘 구성되어 있습니다. 각 스토리마다 명확한 문서화도 포함되어 있습니다.
src/app/(auth)/login/_styles/Login.css.ts (1)
1-4: 적절한 스타일 토큰 사용semantic과 typography 토큰을 올바르게 임포트하여 일관된 디자인 시스템을 유지하고 있습니다.
src/components/ui/GNB/SubGNB/SubGNB.css.ts (1)
6-26: vanilla-extract recipe 잘 활용됨
recipe를 사용하여 background 변형을 깔끔하게 구현했습니다. 기본값 설정도 적절합니다.src/components/ui/TextButton/TextButton.stories.tsx (3)
1-5: SVG 컴포넌트 임포트 성공SVG 파일이 React 컴포넌트로 성공적으로 임포트되고 있어 Webpack 마이그레이션이 올바르게 작동함을 확인할 수 있습니다.
77-91: Custom variant 스토리 설명 훌륭함Custom variant의 용도에 대한 설명이 명확합니다. 이는 개발자들이 언제 custom variant를 사용해야 하는지 이해하는 데 도움이 됩니다.
108-122: leftAddon 사용 예시 적절함leftAddon props의 사용 예시가 명확하고 실용적입니다. 아이콘 크기(20x20)도 적절합니다.
src/components/ui/TextButton/TextButton.css.ts (1)
1-52: 전반적으로 잘 구현되었습니다!vanilla-extract의 recipe 패턴을 활용하여 변형(variant)과 크기(size) 옵션을 깔끔하게 구현하셨네요. 스타일 파일의 위치와 네이밍 규칙도 잘 지켜주셨습니다.
| import { button } from "./Button.css"; | ||
|
|
||
| type ButtonVariant = "primary" | "dark" | "assistive"; | ||
| type ButtonVariant = "primary" | "dark" | "assistive" | "custom"; |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
"custom" 변형이 추가되었습니다. 해당 스타일 구현을 확인해 주세요.
ButtonVariant 타입에 "custom" 변형이 올바르게 추가되었습니다. Button.css.ts에 해당 스타일이 구현되어 있는지 확인이 필요합니다.
다음 스크립트를 실행하여 custom 변형 스타일이 구현되어 있는지 확인해 주세요:
🏁 Script executed:
#!/bin/bash
# custom 변형 스타일 구현 확인
rg -A 10 "custom" src/components/ui/Button/Button.css.tsLength of output: 292
Button.css.ts에 custom 변형 스타일 내용 추가 필요
src/components/ui/Button/Button.css.ts의 custom 키는 이미 정의되어 있지만, 현재 빈 객체로 스타일이 구현되어 있지 않습니다. 아래 위치에 원하는 스타일 속성(색상, 배경, 폰트 등)을 추가해 주세요.
• 파일: src/components/ui/Button/Button.css.ts
• 위치:
variant: {
…
custom: {}, // 여기에 스타일 속성 추가 필요
},🤖 Prompt for AI Agents
In src/components/ui/Button/Button.tsx at line 5, the ButtonVariant type
includes "custom", but in src/components/ui/Button/Button.css.ts, the "custom"
variant is defined as an empty object. To fix this, open
src/components/ui/Button/Button.css.ts and add the desired CSS style properties
(such as color, background, font) inside the "custom" key within the variant
object to properly style the custom button variant.
| import type { Meta, StoryObj } from "@storybook/nextjs"; | ||
| import { type ComponentProps } from "react"; | ||
|
|
||
| import ClearIcon from "@/assets/clear.svg"; |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
SVG 파일 경로를 확인해 주세요.
ClearIcon import가 활성화되었지만 파이프라인에서 '@/assets/clear.svg' 파일을 찾을 수 없다는 오류가 발생하고 있습니다.
다음 스크립트를 실행하여 SVG 파일 존재 여부와 경로 별칭 설정을 확인해 주세요:
🏁 Script executed:
#!/bin/bash
# SVG 파일 존재 여부 확인
echo "=== SVG 파일 찾기 ==="
fd -e svg clear
echo "=== assets 폴더 구조 확인 ==="
fd -t d assets
echo "=== 경로 별칭 설정 확인 ==="
cat tsconfig.json | jq '.compilerOptions.paths'Length of output: 299
SVG 파일 경로 대소문자 불일치 수정 필요
TextField 스토리에서 ClearIcon을 import할 때 실제 파일명은 src/assets/Clear.svg인데, 코드에서는 clear.svg로 소문자 경로를 사용하여 빌드 오류가 발생하고 있습니다. import 구문을 다음과 같이 수정해 주세요:
- import ClearIcon from "@/assets/clear.svg";
+ import ClearIcon from "@/assets/Clear.svg";- 파일 위치:
src/components/ui/TextField/TextField.stories.tsx(4번째 줄)
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import ClearIcon from "@/assets/clear.svg"; | |
| -import ClearIcon from "@/assets/clear.svg"; | |
| +import ClearIcon from "@/assets/Clear.svg"; |
🤖 Prompt for AI Agents
In src/components/ui/TextField/TextField.stories.tsx at line 4, the import
statement for ClearIcon uses an incorrect lowercase file path 'clear.svg' which
does not match the actual file name 'Clear.svg'. Update the import path to use
the correct case-sensitive filename 'Clear.svg' to resolve the build error.
There was a problem hiding this comment.
@wkdtnqls0506 수빈님 요것 때문에 스토리북 실행이 안 돼요! 나중에 수정 부탁드립니다!
There was a problem hiding this comment.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
| webpackFinal: async (config: any) => { | ||
| const imageRule = config.module.rules.find(rule => { | ||
| const test = (rule as { test: RegExp }).test; | ||
|
|
||
| if (!test) { | ||
| return false; | ||
| } | ||
|
|
||
| return test.test(".svg"); | ||
| }) as { [key: string]: any }; | ||
|
|
||
| imageRule.exclude = /\.svg$/; | ||
|
|
||
| config.module.rules.push({ | ||
| test: /\.svg$/, | ||
| use: ["@svgr/webpack"], | ||
| }); | ||
|
|
||
| config.plugins.push(new VanillaExtractPlugin()); | ||
|
|
||
| return config; | ||
| }, |
There was a problem hiding this comment.
🛠️ Refactor suggestion
타입 안전성을 개선해주세요.
webpack 설정에서 any 타입을 과도하게 사용하고 있습니다. 더 명확한 타입을 사용하면 유지보수성이 향상됩니다.
- webpackFinal: async (config: any) => {
- const imageRule = config.module.rules.find(rule => {
- const test = (rule as { test: RegExp }).test;
+ webpackFinal: async (config: Configuration) => {
+ const imageRule = config.module?.rules?.find(rule => {
+ if (typeof rule === "object" && rule !== null && "test" in rule) {
+ const test = rule.test;그리고 타입 가드를 사용하여 안전하게 처리하세요:
- }) as { [key: string]: any };
+ });
- imageRule.exclude = /\.svg$/;
+ if (imageRule && typeof imageRule === "object") {
+ imageRule.exclude = /\.svg$/;
+ }Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In .storybook/main.ts around lines 16 to 37, the webpackFinal function uses
'any' type for the config parameter and casts rules unsafely. Replace 'any' with
the appropriate Webpack Configuration type from '@storybook/core-common' or
'webpack' packages. Use type guards to check if rule.test exists and is a RegExp
before calling test.test(".svg"). This will improve type safety and
maintainability.
| import type { Meta, StoryObj } from "@storybook/nextjs"; | ||
| import { type ComponentProps } from "react"; | ||
|
|
||
| import ClearIcon from "@/assets/clear.svg"; |
There was a problem hiding this comment.
@wkdtnqls0506 수빈님 요것 때문에 스토리북 실행이 안 돼요! 나중에 수정 부탁드립니다!
| export const SubGNB = ({ | ||
| title, | ||
| align = "center", | ||
| leftAddon, | ||
| rightAddon, | ||
| background, | ||
| }: SubGNBProps) => { | ||
| return ( | ||
| <header className={styles.wrapper({ background })}> | ||
| <div | ||
| className={ | ||
| leftAddon && title && align === "left" | ||
| ? `${styles.leftWrapper} ${styles.leftWrapperWithMargin}` | ||
| : styles.leftWrapper | ||
| } | ||
| > | ||
| {leftAddon} | ||
| </div> | ||
| <div | ||
| className={ | ||
| align === "center" | ||
| ? styles.titleWrapperCenter | ||
| : styles.titleWrapperLeft | ||
| } | ||
| > | ||
| {title && <h1 className={styles.title}>{title}</h1>} | ||
| </div> | ||
| <div className={styles.rightWrapper}>{rightAddon}</div> | ||
| </header> | ||
| ); | ||
| }; |
| import { button } from "./Button.css"; | ||
|
|
||
| type ButtonVariant = "primary" | "dark" | "assistive"; | ||
| type ButtonVariant = "primary" | "dark" | "assistive" | "custom"; |
There was a problem hiding this comment.
ASK: custom은 어떤 용도인지 알 수 있을까요?!
There was a problem hiding this comment.
기존 variant 스타일을 덮어쓰기보단 custom으로 초기화 후 원하는 스타일만 적용하는게 깔끔하다고 생각해서 사용했습니다!
| position: "relative", | ||
| width: "100%", | ||
| height: "100%", | ||
| backgroundImage: "url(\"/images/login-background.png\")", |
There was a problem hiding this comment.
P2: Next.js Image를 사용하는 방법은 어떨까요?! 혹은 css backgroundImage를 사용할 경우 해당 이미지를 tinypng같은 곳에서 압축 + webp로 변환하여 제공해보아도 좋을 것 같아요! (1.54mb 짜리라,,)
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/components/ui/GNB/GNB.tsx (1)
67-94: 조건부 스타일링 로직을 개선할 수 있습니다.현재 조건부 스타일링 로직이 복잡하여 가독성이 떨어집니다. 더 명확한 방식으로 개선할 수 있습니다.
다음과 같이 리팩토링을 고려해보세요:
export const GNB = ({ title, align = "center", leftAddon, rightAddon, background = "white", }: GNBProps) => { + const shouldAddLeftMargin = leftAddon && title && align === "left"; + const leftWrapperClassName = shouldAddLeftMargin + ? `${styles.leftWrapper} ${styles.leftWrapperWithMargin}` + : styles.leftWrapper; + + const titleWrapperClassName = align === "center" + ? styles.titleWrapperAbsoluteCenter + : styles.titleWrapperLeft; return ( <header className={styles.wrapper({ background })}> - <div - className={ - leftAddon && title && align === "left" - ? `${styles.leftWrapper} ${styles.leftWrapperWithMargin}` - : styles.leftWrapper - } - > + <div className={leftWrapperClassName}> {leftAddon} </div> {title && ( - <div - className={ - align === "center" - ? styles.titleWrapperAbsoluteCenter - : styles.titleWrapperLeft - } - > + <div className={titleWrapperClassName}> <span className={styles.title}>{title}</span> </div> )} <div className={styles.rightWrapper}>{rightAddon}</div> </header> ); };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
src/assets/circle-clear.svgis excluded by!**/*.svg
📒 Files selected for processing (5)
src/components/ui/GNB/GNB.css.ts(1 hunks)src/components/ui/GNB/GNB.stories.tsx(1 hunks)src/components/ui/GNB/GNB.tsx(1 hunks)src/components/ui/GNB/index.ts(1 hunks)src/components/ui/TextField/TextField.stories.tsx(2 hunks)
✅ Files skipped from review due to trivial changes (3)
- src/components/ui/GNB/index.ts
- src/components/ui/GNB/GNB.css.ts
- src/components/ui/GNB/GNB.stories.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/ui/TextField/TextField.stories.tsx
🧰 Additional context used
📓 Path-based instructions (1)
`{src/app/**/_components/*.{ts,tsx},src/components/**/*.{ts,tsx}}`: 컴포넌트 파일은 PascalCase로 네이밍해야 한다 (예: `Button.tsx`, `DomainLayout.tsx`).
{src/app/**/_components/*.{ts,tsx},src/components/**/*.{ts,tsx}}: 컴포넌트 파일은 PascalCase로 네이밍해야 한다 (예:Button.tsx,DomainLayout.tsx).
📄 Source: CodeRabbit Inference Engine (.cursor/rules/nextjs-folder-structure.mdc)
List of files the instruction was applied to:
src/components/ui/GNB/GNB.tsx
🧬 Code Graph Analysis (1)
src/components/ui/GNB/GNB.tsx (2)
src/components/ui/GNB/index.ts (1)
GNB(1-1)src/components/ui/GNB/GNB.css.ts (1)
title(57-64)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: test
🔇 Additional comments (2)
src/components/ui/GNB/GNB.tsx (2)
5-6: 타입 정의가 명확하고 적절합니다.타입 정의가 명확하고 사용 목적에 부합합니다.
8-35: Props 인터페이스가 잘 구성되어 있습니다.Props 타입 정의가 명확하고 JSDoc 주석이 상세합니다. 모든 props가 optional로 설정되어 유연성을 제공합니다.
Seojunhwan
left a comment
There was a problem hiding this comment.
아이코 제가 놓친 부분까지 잘 챙겨주셨군요! 고생하셨어요!


✅ 이슈 번호
close #37
🪄 작업 내용 (변경 사항)
/loginTextButton공통 컴포넌트 구현SubGNB공통 컴포넌트 구현📸 스크린샷
Login 페이지
💡 설명
🗣️ 리뷰어에게 전달 사항
📍 트러블 슈팅
Summary by CodeRabbit
New Features
Style
Refactor
Chores